/*
 * NewInternal.h
 *
 *  Created on: Oct 28, 2012
 *      Author: Federico Orquera
 */

#ifndef NEWINTERNAL_H_
#define NEWINTERNAL_H_

#include "NewLeaf.h"
#include "../bplusfilehandler/NewHandlerInterface.h"

template <
	typename keyTemplate,
	typename valueTemplate,
	bool keyComparator(
		const keyTemplate& key1,
		const keyTemplate& key2
	)
>
class NewInternal: public NewNode <keyTemplate, valueTemplate, keyComparator>{

public:

	typedef NewNode	<keyTemplate, valueTemplate, keyComparator> Node;

	typedef typename Node::NodeOrder 		NodeOrder;
	typedef typename Node::NodeLevel 		NodeLevel;
	typedef typename Node::NodeID 			NodeID;
	typedef typename Node::registerType 	registerType;
	typedef typename Node::register_ptr 	register_ptr;
	typedef typename Node::searchResult		searchResult;

	typedef NewLeaf				<keyTemplate, valueTemplate, keyComparator> 					 Leaf;
	typedef NewInternal			<keyTemplate, valueTemplate, keyComparator> 					 Internal;
	typedef NewNode				<keyTemplate, NodeID, keyComparator> 							 InternalNodePrototype;
	typedef NewHandlerInterface	< NodeID, NodeLevel, keyTemplate, valueTemplate, keyComparator > NodeHandler;

	typedef typename Leaf::registerPointerList 	leafRegisterPointerList;

	typedef typename InternalNodePrototype::Key_t						InternalKey_t;
	typedef typename InternalNodePrototype::Value_t						InternalValue_t;
	typedef typename InternalNodePrototype::registerType 				InternalRegisterType;
	typedef typename InternalNodePrototype::register_ptr 				InternalRegister_ptr;
	typedef typename InternalNodePrototype::registerPointerList 		InternalRegisterPointerList;
	typedef typename InternalNodePrototype::registerPointerListIterator InternalRegisterPointerListIterator;
	typedef typename InternalNodePrototype::SimpleRegisterList 			InternalSimpleRegisterList;
	typedef typename InternalNodePrototype::registerPointerList_ptr		InternalRegisterPointerList_ptr;

	typedef typename NodeHandler::node_ptr node_ptr;

	struct SerializationBlock{
        NodeLevel                       level;
        NodeID                          firstChild;
        InternalRegisterPointerList_ptr content;
    };

	NewInternal(
			const NodeOrder& nodeOrder,
			const NodeLevel& newNodeLevel,
			const NodeID& newNodeID,
			NodeHandler *newHandler
	): Node(nodeOrder,newNodeLevel,newNodeID){
		initialize( newHandler );
	}

	NewInternal(
			const NodeOrder& nodeOrder,
			const NodeLevel& newNodeLevel,
			const NodeID& newNodeID,
			bool (*isOverflow) (const InternalRegisterPointerList& list),
			bool (*isUnderflow)(const InternalRegisterPointerList& list),
			NodeHandler *newHandler
	): Node( nodeOrder,newNodeLevel,newNodeID ){
		initialize( newHandler, NULL_NODE_ID, isOverflow, isUnderflow );
	}

	NewInternal(
			const NodeOrder& nodeOrder,
			const NodeLevel& newNodeLevel,
			const NodeID& newNodeID,
			const NodeID& newFirstBorn,
			NodeHandler *newHandler
	): Node(nodeOrder,newNodeLevel,newNodeID){
		initialize( newHandler, newFirstBorn );
	}

	NewInternal(
			const NodeOrder& nodeOrder,
			const NodeLevel& newNodeLevel,
			const NodeID& newNodeID,
			const NodeID& newFirstBorn,
			bool (*isOverflow) (const InternalRegisterPointerList& list),
			bool (*isUnderflow)(const InternalRegisterPointerList& list),
			NodeHandler *newHandler
	): Node(nodeOrder,newNodeLevel,newNodeID){
		initialize( newHandler, newFirstBorn, isOverflow, isUnderflow );
	}

	NewInternal(
			const NodeOrder& nodeOrder,
			const NodeLevel& newNodeLevel,
			const NodeID& newNodeID,
			bool (*isOverflow) (const InternalRegisterPointerList& list),
			bool (*isUnderflow)(const InternalRegisterPointerList& list),
			InternalKey_t (*cropKey)(const InternalKey_t& key, const InternalKey_t& prevKey),
			NodeHandler *newHandler
	): Node(nodeOrder,newNodeLevel,newNodeID){
		initialize( newHandler, NULL_NODE_ID, isOverflow, isUnderflow, cropKey );
	}

	NewInternal(
			const NodeOrder& nodeOrder,
			const NodeLevel& newNodeLevel,
			const NodeID& newNodeID,
            const NodeID& newFirstBorn,
			const InternalRegisterPointerList_ptr& newContent,
			bool (*isOverflow) (const InternalRegisterPointerList& list),
			bool (*isUnderflow)(const InternalRegisterPointerList& list),
			InternalKey_t (*cropKey)(const InternalKey_t& key, const InternalKey_t& prevKey),
			NodeHandler *newHandler
	): Node(nodeOrder,newNodeLevel,newNodeID){
		initialize( newHandler, newFirstBorn, isOverflow, isUnderflow, cropKey );
		this->setContent(newContent);
	}

	NewInternal(
			const NodeOrder& nodeOrder,
			const NodeLevel& newNodeLevel,
			const NodeID& newNodeID,
            const NodeID& newFirstBorn,
			const InternalRegisterPointerList_ptr& newContent,
			size_t (*getSize) (const NewInternal* node),
			InternalKey_t (*cropKey)(const InternalKey_t& key, const InternalKey_t& prevKey),
			NodeHandler *newHandler
	): Node(nodeOrder,newNodeLevel,newNodeID){
		initialize( newHandler, newFirstBorn, NULL, NULL, cropKey, getSize );
		this->setContent(newContent);
	}

	NewInternal(
            const NodeOrder& nodeOrder,
            const NodeLevel& newNodeLevel,
            const NodeID& newNodeID,
            size_t (*getSize) (const NewInternal* node),
            InternalKey_t (*cropKey)(const InternalKey_t& key, const InternalKey_t& prevKey),
            NodeHandler *newHandler
    ): Node(nodeOrder,newNodeLevel,newNodeID){
        initialize( newHandler, NULL_NODE_ID, NULL, NULL, cropKey, getSize );
    }

	virtual ~NewInternal(){}

	const size_t getSize(){
		if( getSizeFunction != NULL ) return getSizeFunction(this);
		else return pRegisterList.getList().size();
	}

	const NodeID& getFirstSon() const {
		return this->firstSon;
	}

	void setFirstSon(NodeID newFirstBorn) {
		if(newFirstBorn == this->getNodeID())
			throw NodeExceptions("Node can't sire himself. Time paradox created!");
		this->firstSon = newFirstBorn;
	}

	//const InternalRegisterPointerList_ptr& getContent() const {
	InternalRegisterPointerList_ptr& getContent(){
		return this->pRegisterList.getContent();
	}

	/*
	 * Deja la lista de los primeros resgistros hasta estar en Underflow +1 en el nodo, y devuelve la otra "mitad"
	 */
	InternalRegisterPointerList getHalfList(){

		InternalRegisterPointerList returnList, bufferList;

		while(!this->isUnderflow()){
			bufferList.push_back(pRegisterList.getList().front());
			pRegisterList.getList().pop_front();
		}

		returnList = pRegisterList.getList();
		pRegisterList.getList() = bufferList;

		return returnList;

	}

	NodeResulMessages LoadRegister( const register_ptr& registro ){

		node_ptr nextNode;

		bool updated = false;

		if(this->getFirstSon() == NULL_NODE_ID){
			nextNode = handler->getNewNode( this->getLevel()-1 );
			this->firstSon = nextNode->getNodeID();
			updated = true;
		}else if(this->pRegisterList.getList().size() == 0)
			nextNode = handler->getNode( this->getFirstSon() );
		else
			nextNode = handler->getNode( (this->pRegisterList.getList().back())->getValue() );


		switch( nextNode->LoadRegister( registro ) ){

			// Si no hubo cambios, retornar que no hubo cambios
			case NO_CHANGE:
				break;
			// si hubo cambios, grabar y retornar que no hubo cambios
			case UPDATED:
				handler->saveNode(nextNode);
				break;
			// si hubo overflow, resolver overflow!
			case NODE_OVERFLOW:
				return solveLoadOverflow(nextNode, registro);
			default:
				break;

		}

		return (updated)?UPDATED:NO_CHANGE;

	}

	/*
	 * If the bucket is not full (at most b - 1 entries after the insertion), add the record.
	 * Otherwise, split the bucket.
	 * 		Allocate new leaf and move half the bucket's elements to the new bucket.
	 * 		Insert the new leaf's smallest key and address into the parent.
	 * 		If the parent is full, split it too.
	 * 			Add the middle key to the parent node.
	 * 		Repeat until a parent is found that need not split.
	 * If the root splits, create a new root which has one key and two pointers.
	 */

	NodeResulMessages InsertRegister( const register_ptr& registro ){

		node_ptr nextNode = handler->getNode( this->getNextNode( registro->getKey() ) );

		switch( nextNode->InsertRegister( registro ) ){

			// Si no hubo cambios, retornar que no hubo cambios
			case NO_CHANGE:
				return NO_CHANGE;
			// si hubo cambios, grabar y retornar que no hubo cambios
			case UPDATED:
				handler->saveNode(nextNode);
				return NO_CHANGE;
			// si hubo overflow, resolver overflow!
			case NODE_OVERFLOW:
				return solveOverflow(nextNode);
			default:
				return NO_CHANGE;

		}

	}

	NodeResulMessages UpdateRegister( const register_ptr& registro ){

		node_ptr nextNode = handler->getNode( this->getNextNode( registro->getKey() ) );

		searchResult result = nextNode->SearchRegister(registro->getKey());

		if(!(result.valid || result.nextNode == NULL_NODE_ID))
			nextNode = handler->getNode(result.nextNode);

		switch( nextNode->UpdateRegister( registro ) ){

			// Si no hubo cambios, retornar que no hubo cambios
			case NO_CHANGE:
				return NO_CHANGE;
			// si hubo cambios, grabar y retornar que no hubo cambios
			case UPDATED:
				handler->saveNode(nextNode);
				return NO_CHANGE;
			// si hubo overflow, resolver overflow!
			case NODE_OVERFLOW:
				return solveOverflow(nextNode);
			default:
				return NO_CHANGE;

		}

	}

	NodeResulMessages DeleteRegister( const InternalKey_t& key ){
		return NO_CHANGE;
	}

	searchResult begin(){
		return handler->getNode( this->getFirstSon() )->begin();
	}

	searchResult SearchRegister( const InternalKey_t& key ) {

		node_ptr nextNode = handler->getNode( this->getNextNode( key ) );

		searchResult result = nextNode->SearchRegister(key);

		if(result.valid || result.nextNode == NULL_NODE_ID)return result;

		nextNode = handler->getNode(result.nextNode);

		return nextNode->SearchRegister(key);

	}

	void MostrarContenidoReducido(const string& separator = "") {

		// ID: LVL | SIZE | SON_1(KEY_1)SON_2(KEY_N)SON_N

		string nextSeparator = separator;
		nextSeparator += "\t";

		cout<<separator<<this->n<<": "<<this->l<<" | "<<this->getSize()<<" | "<<this->getFirstSon();

		if( this->getFirstSon() == NULL_NODE_ID )return;

		for ( this->it=pRegisterList.getList().begin() ; this->it != pRegisterList.getList().end(); this->it++ )
			cout<< "("<< (*it)->getKey()<< ")"<< (*it)->getValue();

		cout<<endl;

		node_ptr nextNode = handler->getNode( this->getFirstSon() );
		nextNode->MostrarContenidoReducido(nextSeparator);

		for ( this->it=pRegisterList.getList().begin() ; this->it != pRegisterList.getList().end(); this->it++ ){
			nextNode = handler->getNode( (*it)->getValue() );
			nextNode->MostrarContenidoReducido(nextSeparator);
		}

		cout<<endl;
	}

	void MostrarContenido(const string& separator = "") {

		unsigned int i = 0;
		string nextSeparator = separator;
		nextSeparator += "\t";

		cout<<separator<<"NodeOrder: "	<< this->m;
		cout<<endl;
		cout<<separator<<"NodeLevel: "	<< this->l;
		cout<<endl;
		cout<<separator<<"NodeID: "		<< this->n;
		cout<<endl;
		cout<<separator<<"Size: "		<< pRegisterList.getList().size();
		cout<<endl;

		if( this->getFirstSon() == NULL_NODE_ID )return;

		node_ptr nextNode = handler->getNode( this->getFirstSon() );
		cout<<separator<<"Contents: "	<< endl;
		cout<<separator<<"Hijo "<<i<<": "		<<endl;

		nextNode->MostrarContenido(nextSeparator);
		i++;

		for ( this->it=pRegisterList.getList().begin() ; this->it != pRegisterList.getList().end(); this->it++, i++ ){
			cout<<separator<<"key: "<<(*it)->getKey()	;
			cout<<endl;
			cout<<separator<<"Hijo "<<i<<": "			<<endl;
			nextNode = handler->getNode( (*it)->getValue() );
			nextNode->MostrarContenido(nextSeparator);
		}
	}

	NodeResulMessages internalInsert(const InternalKey_t& key, const NodeID& NodeID  ){

		// Cuando se cree el registro a insertar es donde se hace cropeo de la key si se quiere
		InternalRegister_ptr registro( new InternalRegisterType(key, NodeID) );
		this->pRegisterList.simpleInsertRegister(registro);
		if(this->isOverflow())
			return NODE_OVERFLOW;
		return UPDATED;

	}

	InternalKey_t cropKey(const InternalKey_t& key, const InternalKey_t& prevKey){
		return cropKeyFunction(key,prevKey);
	}

	const InternalRegister_ptr& first() {
		return this->pRegisterList.getList().front();
	}
	const InternalRegister_ptr& last() {
		return this->pRegisterList.getList().back();
	}

    SerializationBlock getSerializationBlock() const{
        SerializationBlock returnBlock;
        returnBlock.level       = this->getLevel();
        returnBlock.firstChild  = this->getFirstSon();
        returnBlock.content     = ( (NewInternal*) this)->getContent();
        return returnBlock;
    }

	NodeResulMessages solveOverflow( node_ptr& nextNode ){

		if(nextNode->getLevel() == 0)
			return solveLeafOverflow(nextNode);

		return solveInternalOverflow(nextNode);

	}

private:
	// Persisitr
	NodeID 						firstSon;
	InternalSimpleRegisterList 	pRegisterList;

	// No Perisistir
	InternalRegisterPointerListIterator  it;
	NodeHandler 				*handler;
	bool (*isOverflowFunction) (const InternalRegisterPointerList& list);
	bool (*isUnderflowFunction)(const InternalRegisterPointerList& list);
	InternalKey_t (*cropKeyFunction)(const InternalKey_t& key, const InternalKey_t& prevKey);
	size_t (*getSizeFunction) 	 (const NewInternal* node);

	bool isOverflow( const float& factor = 1.0 ){
		if( isOverflowFunction != NULL ) return isOverflowFunction(pRegisterList.getList());
		if( getSizeFunction != NULL ) return ( getSizeFunction(this) >= factor*this->m );
		return ( pRegisterList.getList().size() > this->m );
	}

	bool isUnderflow( const float& factor = 0.5 ){
		if( isUnderflowFunction != NULL ) return isUnderflowFunction(pRegisterList.getList());
		if( getSizeFunction != NULL )return ( getSizeFunction(this) <= factor*this->m );
		return ( pRegisterList.getList().size() < this->m/2 );
	}

	void initialize(
			NodeHandler              *newHandler,
			const NodeID&            newFirstBorn = NULL_NODE_ID,
			bool (*isOverflow)       (const InternalRegisterPointerList& list) = NULL,
			bool (*isUnderflow)      (const InternalRegisterPointerList& list) = NULL,
			InternalKey_t (*cropKey) (const InternalKey_t& key, const InternalKey_t& prevKey) = NULL,
			size_t (*getSize)        (const NewInternal* node) = NULL
	){
		this->handler = newHandler;
		this->setFirstSon(newFirstBorn);
		this->isOverflowFunction  = isOverflow;
		this->isUnderflowFunction = isUnderflow;
		this->cropKeyFunction = cropKey;
		this->getSizeFunction = getSize;
	}

	NodeID getNextNode( const InternalKey_t& key ){

		this->pRegisterList.simpleInternalSearch(key);

		if( this->pRegisterList.getIterator() == this->pRegisterList.getList().begin() )
			return this->firstSon;

		return (*--this->pRegisterList.getIterator())->getValue();

	}

	NodeResulMessages solveLeafOverflow(const node_ptr& leaf_ptr){

		Leaf *leaf;
		leaf = (Leaf*)leaf_ptr.get();

		Leaf *newleaf;
		node_ptr newleaf_ptr = handler->getNewNode(0);
		newleaf = (Leaf*) newleaf_ptr.get();

		// Obtengo dejo una hoja en estado Oveflow+1
		leafRegisterPointerList bufferList = leaf->getHalfList();

		newleaf->setContent(bufferList);

		newleaf->setRightSibling(leaf->getRightSibling());
		leaf -> setRightSibling( newleaf->getNodeID() );

		handler->saveNode(leaf_ptr);
		handler->saveNode(newleaf_ptr);

		if(bufferList.size()>0)
			return internalInsert( cropKeyFunction( newleaf->first()->getKey(), leaf->last()->getKey() ), newleaf->getNodeID() );
		else
			return internalInsert( leaf->last()->getKey(), newleaf->getNodeID() );

	}

	NodeResulMessages solveInternalOverflow(const node_ptr& interal_ptr){

		Internal *internal;
		internal = (Internal*)interal_ptr.get();
		// Creo un Nuevo Nodo
		Internal *newInternal;
		node_ptr newinternal_ptr = handler->getNewNode(this->getLevel()-1);
		newInternal = (Internal*) newinternal_ptr.get();

		// Obtengo dejo una hoja en estado Oveflow+1
		InternalRegisterPointerList bufferList = internal->getHalfList();

		// Saco la clave mas baja de la media lista
		InternalRegister_ptr front = bufferList.front();

		//Recupero el primer registro
		InternalRegister_ptr first_ptr = bufferList.front();

		// La elimino de la lista
		bufferList.pop_front();

		// Uso el primer puntero como primer hijo del no nuevo
		newInternal->setFirstSon(front->getValue());

		// Inserto el resto de la lista en el nodo nuevo
		newInternal->setContent(bufferList);

		handler->saveNode(interal_ptr);
		handler->saveNode(newinternal_ptr);

		// Inserto la nueva clave en el nodo padre
		if(bufferList.size()>0)
			//return internalInsert( newInternal->first()->getKey(), internal->last()->getKey() ), newinternal_ptr->getNodeID() );
			return internalInsert( first_ptr->getKey(), newinternal_ptr->getNodeID() );
			//return internalInsert( cropKeyFunction( first_ptr->getKey(), internal->last()->getKey() ), newinternal_ptr->getNodeID() );
		else
		    return internalInsert( internal->last()->getKey(), newinternal_ptr->getNodeID() );

	}

	NodeResulMessages solveLoadOverflow( node_ptr& prevNode, const register_ptr& registro ){

		node_ptr newNode;

		handler->saveNode(prevNode);

		if(this->isOverflow())
			return NODE_OVERFLOW;

		newNode = handler->getNewNode(this->getLevel()-1);

		switch( newNode->LoadRegister(registro) ){
			case NO_CHANGE:
				break;
			case UPDATED:
				handler->saveNode(newNode);
				break;
			default:
				break;
		}

		if(newNode->getLevel() == 0){
			((Leaf*) prevNode.get())->setRightSibling(newNode->getNodeID());
			handler->saveNode(newNode);
			internalInsert( cropKeyFunction( ((Leaf*) newNode.get())->first()->getKey(), ((Leaf*) prevNode.get())->last()->getKey() ), ((Leaf*)     newNode.get())->getNodeID() );
			return UPDATED;
		}else{
			handler->saveNode(newNode);
			internalInsert( registro->getKey(), newNode->getNodeID() );
			return UPDATED;
		}
	}

	void setContent(const InternalRegisterPointerList& newContent) {
		this->pRegisterList.getList() = newContent;
	}

	void setContent(const InternalRegisterPointerList_ptr& newContent) {
		this->pRegisterList.setContent(newContent);
	}

};


#endif /* NEWINTERNAL_H_ */
